home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The 640 MEG Shareware Studio 2
/
The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO
/
pascal
/
sort_stm.zip
/
SINDEXED.PAS
next >
Wrap
Pascal/Delphi Source File
|
1992-10-18
|
18KB
|
627 lines
{---------------------------------------------------------------------------}
{ AUTHOR: Bruce Ruona, FIDONET: 1:2280/1 }
{ Released into Public Domain, October 1992}
{}
{ October 18th, 1992}
{VERSION 1.01 implements folloing changes from previous editions:}
{ EOS: End of stream function in Base stream type}
{}
{ NDXSTREAM: maintains a listing of OFFSETS into MASTER stream}
{ by storing a series of LONGINTs onto an EMS stream}
{ Uses a simple BINARY INSERTION method to speed up search}
{ of current data}
{}
{ SORTEDINDEXSTREAM: over-ride GET method to use NDXSTREAM offset}
{ ITEMPOS method to position to specified ITEM #}
{ DONE Destructor over-ride to also dump Ndxstream}
{ A few other necessary new procs/methods also added}
{}
{ BENCHMARKS: now creates and stores in excess of 1,200 base objects/Minute}
{ maximum Search on any Insertions: 8.6 times AVG.}
{---------------------------------------------------------------------------}
{}
Unit Sindexed;
{Provides a searchable/Indexed & Searchable Stream}
Interface
Uses Dos,
Crt,
Objects;
Type
{our Base type for all searchable objects}
{all Searchable objects must be derived from this!}
pIndex = ^indexobj;
IndexObj = OBJECT(Tobject)
{compare testobj against current for equality}
{base object calls ABSTRACT error if called!!}
Function Compare(T: pIndex): INTEGER; VIRTUAL;
end;
{a searchable stream that Knows a file name, INIT will load data to stream}
{from file name, DONE will dump data back to file}
pIndexed = ^indexedstream;
IndexedStream = OBJECT(tEmsStream)
FileName: Pstring;
ItemCount: Longint; {count of Items in stream}
Constructor Init(amin: Longint; fname: Pathstr);
Destructor Done; VIRTUAL;
Procedure Flush; VIRTUAL;
Function Find(SearchObj: pIndex): Boolean; VIRTUAL;
{over-ride Put to provide mangement of ITEMCOUNT variable}
Procedure Put(P: pObject);
{Identical to EOF function for Files}
Function EOS: Boolean; VIRTUAL;
end;
{User definable STATUS display for call to ndxstream.Reindex method,}
{which may take a minute or two to complete}
NdxDisplayProc = Procedure(CurrPos:LongInt;Col,Row: Byte);
{a Descendent of tEMSStream, provides a sorted list of offsets into}
{master stream allowing easy searches by using a BINARY Insertion method}
{and storing a simple list of LONGINT's on stream}
pNdxStream = ^NdxStream;
NdxStream = OBJECT(tEMSStream)
owner: pIndexed; {a pointer back to our master stream note pointer to ANCESTOR}
CP: Longint; {Current ITEM NUMBER of Master index}
fname: pathstr; {File name for saving index}
displayProc: NdxDisplayProc; {Procedure to call on REINDEX Routine}
Constructor Init(master: pStream; Amin: longint; NdxFile: Pathstr);
Destructor Done; VIRTUAL;
Function EOS: Boolean;
{rebuild our index from Scratch}
Procedure Reindex;
{inserts a new key into stream}
Procedure Insert(Key: Pindex; KeyPos: LongInt);
{searches for KEY in master stream using offsets}
Function Search(Key: pIndex; VAR AtPos: LongInt): Boolean;
{returns total number of Items in list, from 0 to N+1}
Function Count: Longint;
Function At: Longint; {returns OFFSET of CP [Current item]}
{updates CP for SEEK activity on MASTER LIST}
Procedure Pos(P: longInt);
{updates CP for GET activity on Master List}
Procedure NextPos;
{Dump index to named file}
Procedure Flush; VIRTUAL;
end;
{a descendent of indexedstream that inserts data into a sorted order based}
{on result of pindex compare method, NOte that the ITEMPOS method should}
{be used in the main program in place of SEEK especially to reset to beg.}
pSortedIndex = ^SortedIndexStream;
SortedIndexStream = Object(IndexedStream)
indexList: pNdxStream;
Constructor Init(amin: Longint; fname: Pathstr);
Destructor Done; VIRTUAL;
Procedure Insert(Key: pIndex);
Function Find(SearchObj: pIndex): Boolean; VIRTUAL;
Function Get: pObject; VIRTUAL;
{similar to SEEK but seeks to ITEM Number instead of OFFSET}
Procedure ItemPos(ItemNum: Longint);
{simply provides a calling point for INDEXLIST reindex method}
Procedure Rebuild;
{Simply returns COUNT from IndexList}
Function Count: Longint;
end;
implementation
{our base object COMPARE procedure}
Function IndexObj.Compare(T:pIndex): Integer;
begin
Abstract; {call ABSTRACT error procedure if called!}
end;
{a procedure to call to display status during a lengthy REINDEX call}
{$F+}
Procedure OurDisplayProc(CurrPos: Longint;Col,Row: Byte);
begin
Gotoxy(Col,Row);
Write('REINDEXING:...',Currpos:8);
end;
{$F-}
{=========================================================================}
{ Indexstream }
{=========================================================================}
CONSTRUCTOR IndexedStream.Init(Amin: Longint; Fname: Pathstr);
Var TmpStream: tBufStream;
InitSize: Longint;
begin
tEmsStream.Init(Amin,MaxLongInt);
if (Fname='') or (status<>StOK) then
FAIL
else
begin
FileName:=NewStr(Fname);
ItemCount:=0;
TmpStream.INIT(Fname,STOpen,8*1024);
if TmpStream.Status=STOK then
begin
InitSize:=TmpStream.Getsize;
Seek(0); {position our stream...}
TmpStream.Seek(0); {position temp stream to start}
copyfrom(Tmpstream,initsize);
ItemCount:=0;
Seek(0);
end;
tmpstream.done;
end;
end;
DESTRUCTOR IndexedStream.Done;
begin
Flush; {save EMS back to file}
if Filename<>NIL then
disposeStr(filename);
tEmsStream.done;
end;
{Flushes our EMS stream back to named DISK file}
PROCEDURE IndexedStream.Flush;
var BUFF: tBufStream;
begin
buff.Init(Filename^,stCreate,8*1024);
if Buff.Status=StOK then
begin
Seek(0);
Buff.CopyFrom(Self,GetSize);
end;
buff.done;
end;
{Searches Stream for object Searchobj, returns FALSE if NOT found}
Function IndexedStream.Find(SearchObj: Pindex): Boolean;
VAR Temp: pIndex;
found: Boolean;
begin
seek(0);
Found:=False;
while (GetPos<GetSize) and NOT FOUND do
begin
Temp:=Pindex(get);
if (Temp<>NIL) AND (Temp^.Compare(Searchobj)=0) then
begin
if Temp<>NIL then
Found:=TRUE;
end;
if Temp<>NIL then
dispose(Temp,done);
end;
Find:=Found;
end;
Procedure IndexedStream.Put(p: pObject);
begin
tEmsStream.Put(p);
inc(ItemCount);
end;
Function IndexedStream.EOS: Boolean;
{returns TRUE if at _End _Of _Stream}
begin
EOS:=(GetPos>=GetSize)
end;
{=========================================================================}
{ SortedIndexStream }
{=========================================================================}
Constructor SortedIndexStream.Init(amin: Longint; fname: Pathstr);
{compute NDX name for associated index file based on passed fname}
Function IndexName: PathStr;
VAR DS: DirStr;
NS: NameStr;
Es: Extstr;
begin
if FName<>'' then
begin
Fsplit(Fname,Ds,Ns,Es);
IndexName:=DS+NS+'.NDX';
end
else IndexName:='TEMP.NDX';
end;
begin
IndexedStream.Init(amin,fname);
IndexList:=NIL;
ItemCount:=0;
if Status<>StOK then FAIL else
begin
{initialize our Base index list with a 64K EMS Buffer}
IndexList:=NEW(pNdxStream,INIT(@Self,64*1024,IndexName));
if (IndexList=NIL) or (IndexList^.Status<>StOK) then FAIL;
if Count<>0 then begin;end; {just to set our ITEMCOUNT var.}
end;
end;
Destructor SortedIndexStream.Done;
begin
{dispose of Indexlist, and dump index list to computed named file}
if IndexList<>NIL then
Dispose(indexList,Done);
IndexList:=NIL;
IndexedStream.Done;
end;
Procedure SortedIndexStream.Insert(Key: pIndex);
VAR P: Longint;
begin
P:=GetSize;
Seek(GetSize); {Appending at end of file}
Put(Key);
if IndexList<>NIL then
IndexList^.Insert(Key,P);
end;
{Search method, only searches until found or item>Searchobj}
Function SortedIndexStream.Find(SearchObj: pIndex): Boolean;
VAR Temp: pIndex;
found: Boolean;
Result: Integer;
P: Longint;
begin
ItemPos(0);
Found:=False;
Find:=IndexList^.Search(SearchObj,P);
end;
Function SortedIndexStream.Get: pObject;
begin
Get:=NIL;
{get offset of IndexList CP, and postion Stream to point}
if IndexList<>NIL then
seek(IndexList^.At);
{end of stream?}
if NOT EOS then
begin
Get:=IndexedStream.Get;
if IndexList<>NIL then
begin
IndexList^.NextPos;
if IndexList^.At<=GetSize then
seek(IndexList^.At)
else
Seek(GetSize);
end;
end;
end;
Procedure SortedIndexStream.ItemPos(ItemNum: Longint);
begin
if IndexList<>NIL then
begin
if ItemNum<IndexList^.COUNT then
IndexList^.CP:=ItemNum;
Seek(IndexList^.At);
end
Else seek(GetSize);
end;
Procedure SortedIndexStream.Rebuild;
begin
If IndexList<>NIL then
IndexList^.Reindex;
end;
{Updates ItemCount, and provides direct call into indexlist.count}
Function SortedIndexStream.Count: Longint;
begin
If IndexList<>NIL then
ItemCount:=IndexList^.count
else
ItemCount:=-1;
Count:=ItemCount;
end;
{=========================================================================}
{ NdxStream Methods }
{=========================================================================}
Constructor NdxStream.Init(Master:pStream; Amin: longint; NdxFile: Pathstr);
VAR Buff: tBufStream;
begin
Owner:=NIL;
tEmsStream.INIT(Amin,MaxLongInt);
if Status<>StOK then FAIL {fail if not enough EMS}
else
begin
Owner:=pSortedIndex(master); {typecast our owner field to actual type}
Fname:=NdxFile;
buff.Init(Fname,stOpen,8*1024);
if Buff.Status=StOK then
begin
Seek(0);
Buff.seek(0);
CopyFrom(BUff,Buff.GetSize);
Seek(0);
cp:=0;
end
else
begin
{error Reading .NDX file!!}
Reindex; {rebuild our master list}
Owner^.Seek(0);
end;
buff.done;
Owner^.Seek(0);
CP:=0;
end;
DisplayProc:= OurDisplayProc;
end;
Destructor NdxStream.Done;
begin
flush;
tEmsStream.done;
end;
{save index file to stream}
Procedure NdxStream.Flush;
var BUFF: tBufStream;
begin
buff.Init(Fname,stCreate,8*1024);
if Buff.Status=StOK then
begin
Seek(0);
Buff.CopyFrom(Self,GetSize);
end;
buff.done;
end;
Function NdxStream.EOS: Boolean;
begin
EOS:=(GetPos>=GetSize);
end;
{returns TRUE if found in NDX STREAM, insertion point or actual offset}
{returned in ATPos}
Function NdxStream.Search(Key: pIndex; VAR AtPos: LongInt): Boolean;
VAR Temp: pIndex;
found: Boolean;
SearchDir: Integer;
result: Integer;
TotalPos, {TOP}
LastSearch, {BOTTOM}
CurrentSearch, {MIDDLE}
SeekPos,
CurrentPos,
SavePos: LongInt;
begin
Seek(0);
CP:=0;
Searchdir:=1;
if Count>0 then
TotalPos:=Count-1 {returns total item's in Stream}
else
begin
TotalPos:=0;
SearchDir:=0;
end;
LastSearch:=0;
CurrentSearch:=(LastSearch+TotalPos) DIV 2;
Search:=FALSE;
AtPos:=GetSize; {default insert at end of stream}
Temp:=NIL;
Found:=FALSE;
While NOT FOUND and (SearchDir<>0) do
begin
SavePos:=CurrentSearch*sizeof(LongInt);
Seek(SavePos);
CP:=(SavePos DIV SizeOf(LongInt))-1;
CurrentPos:=Position; {Track our insert position}
Read(SeekPos,sizeof(LongInt)); {Get pos. of next object on Owner}
if SeekPos<Owner^.GetSize then {Don't Attempt to read pas EOS!}
begin
Owner^.Seek(SeekPos);
Temp:=Pindex(Owner^.get);
if Owner^.Status = STOk then
begin
result:=Temp^.Compare(key);
Found:=(result=0);
if (Result>=0) and (currentPos<=AtPos) then
AtPos:=CurrentPos;
end
else
Owner^.Reset; {oh oh! Something BAD happaned on our read!}
if Temp<>NIL then
dispose(Temp,Done);
end;
{compute Next search position}
if NOT FOUND then
begin
if Result>0 then
TotalPos:=CurrentSearch-1
else
LastSearch:=CurrentSearch+1;
if (TotalPos<lastSearch) or (TotalPos<0) then
SearchDir:=0;
end;
CurrentSearch:=(LastSearch+TotalPos) DIV 2;
end;
Search:=Found;
end;
{insert a new index key into stream, calls SEARCH to locate position of ins.}
Procedure NdxStream.Insert(Key: pIndex; KeyPos: Longint);
CONST
tempname = '$$$$$$$$.$$$'; {name for temporary backup file}
VAR Temp: pIndex;
filebuff,
found: Boolean;
result: Integer;
seekPos,
copysize: Longint;
pStrm: Pstream;
F: File;
begin
if Search(Key,SeekPos) then
begin {don't care, We'll ALLOW Duplicate insertions, the important thing}
end; {is the SeekPos Variable Which tells us our position to insert at}
if SeekPos>=GetSize then
begin
{NOT found in file so append to end...}
seek(GetSize);
Write(KeyPos,Sizeof(LongInt));
CP:=Count-1;
end
else
begin
filebuff:=False;
Seek(SeekPos);
CopySize:=Getsize-SeekPos;
Pstrm:=NEW(pEmsStream,INIT(CopySize,maxlongint));
if (Pstrm=NIL) or (Pstrm^.Status<>StOk) then
begin
if pStrm<>NIL then
dispose(pStrm,Done);
{our Attempt to create a temporary EMS stream failed!}
{create a temporary DOS file for copying/appending...}
pStrm:=NEW(pBufStream,Init(TempName,StCreate,16*1024));
filebuff:=TRUE;
end;
if pStrm^.Status=StOK then
begin
Seek(SeekPos);
CP:=(SeekPos DIV SizeOf(LongInt))-1;
copySize:=(Getsize-SeekPos);
{copy remaining bytes to temporary file}
pStrm^.copyFrom(Self,Copysize);
pStrm^.Flush;
{store record}
Seek(SeekPos);
Write(KeyPos,Sizeof(longInt));
{re-append from temp file}
pStrm^.seek(0);
copyfrom(pStrm^,CopySize);
Dispose(Pstrm,done);
{erase temporary file if dos bufferred}
if FileBuff then
begin
{$I-}
assign(F,TempName);
erase(F);
{$I+}
if IoResult<>0 then begin; end; {don't really care (much!)}
end;
end;
end;
End;
{rebuilds our index order, called if NDX file doesn't exist or at}
{users request, benchmark: 6000 items in 2+ minutes}
Procedure NdxStream.Reindex;
VAR Temp: pIndex;
StartPos,
current,
P,Result: Longint;
X,Y: Byte;
begin
writeln;
X:=WhereX;
Y:=WhereY;
Temp:=NIL;
Seek(0);
CP:=0;
Truncate; {dispose of any previous data}
Owner^.Seek(0); {reposition master stream}
While NOT Owner^.EOS do
begin
displayProc(CP,X,Y);
StartPos:=Owner^.Position;
Temp:=pIndex(Owner^.Get);
Current:=Owner^.Position;
if Owner^.Status=StOk then
begin
Insert(temp,StartPos);
if Temp<>NIL then
begin
dispose(Temp,Done);
Temp:=NIL;
end;
end;
Owner^.Seek(Current);
end;
end;
{returns number of items in index list}
Function NdxStream.Count: LongInt;
begin
Count:=Size DIV SizeOf(LongInt);
end;
Function NdxStream.AT: Longint;
VAR p: Longint;
begin
P:=CP*SizeOf(LongInt);
if P>=Size then
AT:=Owner^.GetSize
else
begin
seek(P);
Read(P,SizeOf(LongInt));
AT:=P;
end;
end;
Procedure NdxStream.POS(P: Longint);
VAR L: LongInt;
I: Longint;
Top, Middle,Bottom: Longint;
begin
seek(0);
Cp:=0;
L:=-1;
I:=0;
Bottom:=0;
Top:=Count;
middle:=Top DIV 2;
if Size>0 then
While (Bottom<=Top) and (L<>P) do
begin
Cp:=Middle;
Seek(Middle*sizeOf(LongINt));
Read(L,SizeOf(LongINt));
if P<L then
Top:=Middle-1
else Bottom:=Middle+1;
Middle:=(Bottom+Top) DIV 2
end;
end;
Procedure NDXStream.NextPos;
begin
if CP<=count then
INC(CP)
else Owner^.Seek(Owner^.GetSize);
end;
end.